/**
    @author: administrator
    @date:   2020/12/4
    @note:
**/
package impl

import (
	"errors"
	"github.com/gorilla/websocket"
	"sync"
)

type Connection struct {
	wsConn *websocket.Conn	//webSocket 句柄
	inChan chan []byte	// 接收客户端消息协程
	outChan chan []byte // 发消息给客户端协程
	closeChan chan int  // 协程间通讯  通知其他协程关闭
	isClosed bool // 状态锁 仅允许执行一次关闭 channel
	mutex sync.Mutex //加锁
}

//初始化webSocket服务
func InitConnection(wsConn *websocket.Conn) (conn *Connection,err error){
	conn = &Connection{
		wsConn:  wsConn,
		inChan:  make(chan []byte,1000),
		outChan: make(chan []byte,1000),
		closeChan: make(chan int,1),
		isClosed:false,
	}

	//启动收消息协程
	go conn.readLoop()

	//启动发消息协程
	go conn.writeLoop()

	return
}

//读取消息API
func (conn *Connection) ReadMessage() (data []byte,err error){
	select{ // 防止inChan 无数据 客户端等待卡死
	case data=<-conn.inChan:
		case <-conn.closeChan:
			err = errors.New("connection is closed")
	}
	return
}

//写入消息API
func (conn *Connection) WriteMessage(data []byte)(err error){
	select{
	case conn.outChan<-data:
		case<-conn.closeChan:
			err = errors.New("connection is closed")
	}
	return
}

//关闭链接
func (conn *Connection) Close(){
	// 线程安全，可重入的Close
	conn.wsConn.Close()

	//关闭chan 这行代码只能执行一次
	conn.mutex.Lock() //加锁
	if !conn.isClosed{
		close(conn.closeChan)
		conn.isClosed = true
	}
	conn.mutex.Unlock() //释放锁
}

//内部实现

// 循环读取客户端消息
func(conn *Connection) readLoop(){
	var (
		data []byte
		err error
	)
	for{
		// 得到客户端消息
		if _,data,err=conn.wsConn.ReadMessage();err!=nil{
			goto ERR
		}
		//将数据写入
		select{
		case conn.inChan <- data:
			case <-conn.closeChan:
				//closeChan 关闭
				goto ERR
		}
	}
	ERR:
		//TODO 如果出错 就关闭链接
		conn.Close()
}

//循环发送消息给客户端
func(conn *Connection) writeLoop(){
	var (
		data []byte
		err error
	)
	for  {
		// 取数据
		select{
		case data = <- conn.outChan:
			case <-conn.closeChan:
				goto ERR
		}
		// 发送给客服端
		if err = conn.wsConn.WriteMessage(websocket.TextMessage,data);err!=nil{
			goto ERR
		}
	}

	ERR:
		//TODO 如果出错 就关闭链接
		conn.Close()
}